#include <openssl/engine.h>
#include <openssl/evp.h>
#include <iostream>
#include <cstring>

extern "C" {

static EVP_PKEY_METHOD* fake_sm2_pkey_meth = nullptr;

// ----- Fake callback implementations -----

static int fake_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
    std::cout << "[fake_engine] ctrl called, type = " << type << std::endl;
    return 1;
}

static int fake_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value) {
    std::cout << "[fake_engine] ctrl_str called, type = " << type << ", value = " << value << std::endl;
    return 1;
}

static int fake_sign_init(EVP_PKEY_CTX *ctx) {
    std::cout << "[fake_engine] sign_init" << std::endl;
    return 1;
}

static int fake_sign(EVP_PKEY_CTX* ctx, unsigned char* sig, size_t* siglen,
                     const unsigned char* tbs, size_t tbslen) {
    std::cout << "[fake_engine] sign called, tbslen = " << tbslen << std::endl;

    if (!siglen)
        return 0;

    if (!sig) {
        *siglen = 64;  // Fake SM2 signature length
        return 1;
    }

    memset(sig, 0x42, 64);  // Fill dummy signature with 'B'
    *siglen = 64;

    std::cout << "[fake_engine] returned dummy signature" << std::endl;
    return 1;
}

static int fake_verify_init(EVP_PKEY_CTX *ctx) {
    std::cout << "[fake_engine] verify_init" << std::endl;
    return 1;
}

static int fake_verify(EVP_PKEY_CTX *ctx, const unsigned char* sig, size_t siglen,
                       const unsigned char* tbs, size_t tbslen) {
    std::cout << "[fake_engine] fake_verify called, siglen = " << siglen
              << ", tbslen = " << tbslen << std::endl;
    return 1;  // Always successful
}

static int fake_digest_custom(EVP_PKEY_CTX* ctx, EVP_MD_CTX* mctx) {
    std::cout << "[fake_engine] digest_custom" << std::endl;
    return 1;
}

// ----- NID Mapping -----

static int fake_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
                           const int **nids, int nid) {
    static int sm2_nids[] = { EVP_PKEY_SM2, 0 };

    if (!pmeth) {
        std::cout << "[fake_engine] fake_pkey_meths get nids" << std::endl;
        *nids = sm2_nids;
        return 1;
    }

    std::cout << "[fake_engine] fake_pkey_meths called for nid=" << nid << std::endl;
    if (nid == EVP_PKEY_SM2) {
        *pmeth = fake_sm2_pkey_meth;
        std::cout << "[fake_engine] returning fake_sm2_pkey_meth" << std::endl;
        return 1;
    }

    *pmeth = nullptr;
    return 0;
}

// ----- Bind Entry Point -----

static int bind_fake(ENGINE *e, const char *id) {
    std::cout << "[fake_engine] bind_fake called with id=" << id << std::endl;

    if (!ENGINE_set_id(e, "fake") ||
        !ENGINE_set_name(e, "Fake Engine for SM2 Testing")) {
        return 0;
    }

    const EVP_PKEY_METHOD *default_meth = EVP_PKEY_meth_find(EVP_PKEY_SM2);
    if (!default_meth) {
        std::cerr << "Failed to find default SM2 method" << std::endl;
        return 0;
    }

    fake_sm2_pkey_meth = EVP_PKEY_meth_new(EVP_PKEY_SM2, 0);
    if (!fake_sm2_pkey_meth) {
        std::cerr << "Failed to create new SM2 pkey method" << std::endl;
        return 0;
    }

    EVP_PKEY_meth_copy(fake_sm2_pkey_meth, default_meth);

    // Replace key callbacks
    EVP_PKEY_meth_set_ctrl(fake_sm2_pkey_meth, fake_ctrl, fake_ctrl_str);
    EVP_PKEY_meth_set_sign(fake_sm2_pkey_meth, fake_sign_init, fake_sign);
    EVP_PKEY_meth_set_verify(fake_sm2_pkey_meth, fake_verify_init, fake_verify);
    EVP_PKEY_meth_set_digest_custom(fake_sm2_pkey_meth, fake_digest_custom);

    if (!ENGINE_set_pkey_meths(e, fake_pkey_meths)) {
        std::cerr << "Failed to set pkey_meths" << std::endl;
        EVP_PKEY_meth_free(fake_sm2_pkey_meth);
        fake_sm2_pkey_meth = nullptr;
        return 0;
    }

    std::cout << "[fake_engine] bind_fake setup successful" << std::endl;
    return 1;
}

IMPLEMENT_DYNAMIC_BIND_FN(bind_fake)
IMPLEMENT_DYNAMIC_CHECK_FN()

} // extern "C"
